#include "Client.h"
#include "ClientConnect.h"
#include "BlockRender.h"

Client::Client(){
	gameTicks = 0;
	levelType = NULL;
	serverMode = 0;
	currentDimension = 0;
	difficulty = 0;
	playersList = NULL;
	playersPing = NULL;
	setMaxPlayers(20);
	options.fullscreen = false;
//	options.opengl_mode = GL_MODE_FAST;
	options.opengl_mode = GL_MODE_BASIC;
	options.debug = true;
	options.extended_view = 0;
	if(geGetNumCPU() > 1){
		options.multicore = true;
	}
	
	keys = geCreateKeys();
	LoadResources((char*)"resources");
	Material::_materials_init(this);
	geTextureMode(res.terrain, GE_NEAREST);
	gePrintDebug(0, "Resources loaded\n");

#ifdef ANDROID
	options.view_distance = 64;
#else
	options.view_distance = 256;
#endif
	if(options.opengl_mode != GL_MODE_FAST){
	//	options.extended_view = 256;
	}
	world = new World(this, 42, std::max(options.view_distance, options.extended_view));

#ifdef ANDROID
	world->win = geGuiCreateWindow("LibGE", -1, -1, GE_WINDOW_FULLSCREEN | GE_WINDOW_FULLWINDOW | GE_WINDOW_TRANSPARENT);
	geGuiStyleFont(world->win->style, world->res.font, 20);
	world->b_up = geGuiCreateButton("UP", 64, 64);
	world->b_down = geGuiCreateButton("DOWN", 64, 64);
	world->b_forward = geGuiCreateButton("FORW", 64, 64);
	world->b_backward = geGuiCreateButton("BACK", 64, 64);
	world->b_left = geGuiCreateButton("LEFT", 64, 64);
	world->b_right = geGuiCreateButton("RIGHT", 64, 64);
	geGuiWindowLinkObject(world->win, 0, 580, world->b_up, 0);
	geGuiWindowLinkObject(world->win, 0, 644, world->b_down, 0);
	geGuiWindowLinkObject(world->win, geGetContext()->width-64*2, 564, world->b_forward, 0);
	geGuiWindowLinkObject(world->win, geGetContext()->width-64*2, 628, world->b_backward, 0);
	geGuiWindowLinkObject(world->win, geGetContext()->width-64*3, 628, world->b_left, 0);
	geGuiWindowLinkObject(world->win, geGetContext()->width-64*1, 628, world->b_right, 0);
#endif

	cam = geCreateCamera();
	geCameraSetMaximumAngles(cam, -89, 89, GE_CAMERA_VERTICAL);
	
	threadChunks = new ThreadChunks(this);
	threadGame = new ThreadGame(this);
	threadCtrl = new ThreadCtrl(this);
	threadVisibility = new ThreadVisibility(this);
	threadRender = new ThreadRender(this);
	connect = new ClientConnect(this);
	player = new Player(this);
	player->setLocation(Location(1571.0, -674.0, 100.0));
	player->setStance(1.65);
	player->setName("Player");
	player->setOnGround(false);
//	world->SpawnEntity(player);
	inventoryWindow = new InventoryWindow(this, "Inventory", 340, 320);

	/*
	Player* p = new Player(this);
	p->setId(42);
	p->setLocation(Location(1571.0, -674.0, 100.0));
	p->setStance(1.65);
	p->setName("test");
	p->setOnGround(false);
	p->setFlying(true);
	world->SpawnEntity(p);
	*/

	menu = new MainMenu(this);
}

Client::~Client(){
}

static char _tmp[4096] = "";
static char* catpath(const char* path, const char* file){
	sprintf(_tmp, "%s/%s", path, file);
	return _tmp;
}

static void printNode(ge_AnimNode* node, int n){
	for(int i=0; i<n; i++)printf("  ");
	printf("'%s'\n", node->name);
	for(int i=0; i<node->nChilds; i++){
		printNode(&node->childs[i], n + 1);
	}
}


void PrintMatrix(const char* name, float* m, int s){
	gePrintDebug(0x100, "%s = {\n", name);
	int i = 0;
	for(i=0; i<s; i++){
		gePrintDebug(0x100, "  %f %f %f %f\n", m[i*4+0], m[i*4+1], m[i*4+2], m[i*4+3]);
	}
}
void Client::LoadResources(char* path){
	res.sun = geCreateLight(0.0, 0.0, 0.0, RGBA(255, 255, 255, 255), 0);

	res.font = geLoadFont(catpath(path, "font/Minecraftia.ttf"));
	geFontSize(res.font, 20);
	res.gui = geLoadImage(catpath(path, "gui/gui.png"));
	
	res.shader_lod = geCreateShader();
	geShaderLoadVertexSource(res.shader_lod, "shaders/lod.vert");
	geShaderLoadFragmentSource(res.shader_lod, "shaders/lod.frag");
	res.shader_base = geCreateShader();
	if(options.opengl_mode == GL_MODE_FAST){
		geShaderLoadVertexSource(res.shader_base, "shaders/fast.vert");
		geShaderLoadFragmentSource(res.shader_base, "shaders/fast.frag");
	}else{
		geShaderLoadVertexSource(res.shader_base, "shaders/basic.vert");
		geShaderLoadFragmentSource(res.shader_base, "shaders/basic.frag");
	}

	res.render_base = geCreateRenderer(res.shader_lod);
	geRendererCreateContext(NULL, res.render_base);
	res.loc_wind = geShaderUniformID(res.shader_base, "wind");
	res.loc_chunk_pos = geShaderUniformID(res.shader_base, "chunk_pos");
	res.loc_global_illum = geShaderUniformID(res.shader_base, "global_illum");

	res.cube = geLoadObject("resources/models/cube.obj");
	res.stairs = geLoadObject("resources/models/stairs.obj");
	res.rail = geLoadObject("resources/models/rail.obj");
	
	
	//	geDebugMode(GE_DEBUG_ALL);
	res.entities.shader = geCreateShader();
	geShaderLoadVertexSource(res.entities.shader, "default_shaders/generic_flat_texture.vert");
	geShaderLoadFragmentSource(res.entities.shader, "default_shaders/generic_flat_texture.frag");
	res.entities.render = geCreateRenderer(res.entities.shader);

	int nObjs = geObjectsCountInFile("resources/models/steve.obj");
	ge_Object** objs = (ge_Object**)geMalloc(sizeof(ge_Object*) * nObjs);
	for(int a=0; a<nObjs; a++){
		objs[a] = (ge_Object*)geMalloc(sizeof(ge_Object));
	}
	geLoadObjectsList("resources/models/steve.obj", NULL, 0, objs, NULL);
	for(int a=0; a<nObjs; a++){
		geTextureMode(objs[a]->material.textures[0], GE_NEAREST);
		geRendererLinkObject(res.entities.render, objs[a]);
		if(!strncmp(objs[a]->name, "steve_", 6)){
			res.entities.steve.push_back(objs[a]);
		}
	}

	geRendererCreateContext(NULL, res.entities.render);
	
	res.shader_pick = geCreateShader();
	geShaderLoadVertexSource(res.shader_pick, "shaders/pick.vert");
	geShaderLoadFragmentSource(res.shader_pick, "shaders/pick.frag");
	res.render_pick = geCreateRenderer(res.shader_pick);
	geRendererLinkObject(res.render_pick, res.cube);
	res.render_pick->blend_enabled = true;
	// Pick-block color
	for(int i=0; i<res.cube->nVerts; i++){
		SET_COLOR(res.cube->verts[i].color, RGBA(128, 128, 255, 64));
	}
	geRendererCreateContext(NULL, res.render_pick);
}

void Client::Solo(){
	geCursorRoundMode(true);
//	geCursorVisible(false);
	
	world->setGameMode(SOLO);
	threadCtrl->Start();
	threadChunks->Start();
	threadVisibility->Start();
	threadGame->Start();

	player->setFlying(true);
	setDayTicks(12000);

	ClientThread::entry(sizeof(void*), threadRender);
	
	threadChunks->Stop();
	threadGame->Stop();
	threadCtrl->Stop();
	world->setGameMode(-1);
}

void Client::Multiplayer(){
	printf("Multi 1\n");
	geCursorRoundMode(true);
//	geCursorVisible(false);
	
	printf("Multi 2\n");
	world->setGameMode(MULTI);
	printf("Multi 3\n");
	threadCtrl->Start();
	printf("Multi 4\n");
	threadChunks->Start();
	printf("Multi 5\n");
	threadVisibility->Start();
	printf("Multi 6\n");
	
//	connect->Join("127.0.0.1", 25565);
	connect->Join("78.246.55.93", 25565);
	printf("Multi 7\n");
//	connect->Join("88.191.238.30", 25722);
//	connect->Join("193.138.125.75", 25592);
	
	threadGame->Start();
	printf("Multi 8\n");
	
	ClientThread::entry(sizeof(void*), threadRender);

	threadChunks->Stop();
	threadGame->Stop();
	threadCtrl->Stop();
	world->setGameMode(-1);
	
}

ThreadChunks* Client::getThreadChunks(){
	return (ThreadChunks*)threadChunks;
}

ThreadGame* Client::getThreadGame(){
	return (ThreadGame*)threadGame;
}

ThreadCtrl* Client::getThreadCtrl(){
	return (ThreadCtrl*)threadCtrl;
}

ThreadRender* Client::getThreadRender(){
	return (ThreadRender*)threadRender;
}

ThreadVisibility* Client::getThreadVisibility(){
	return (ThreadVisibility*)threadVisibility;
}

ClientConnect* Client::getClientConnect(){
	return connect;
}

InventoryWindow* Client::getInventoryWindow(){
	return inventoryWindow;
}

Player* Client::getPlayer(){
	return player;
}

World* Client::getWorld(){
	return world;
}

u64 Client::getGameTicks(){
	return gameTicks;
}

void Client::setGameTicks(u64 t){
	gameTicks = t;
}

u64 Client::getDayTicks(){
	return dayTicks;
}

void Client::setDayTicks(u64 t){
	dayTicks = t % 24000;
}

void Client::setLevelType(const char* type){
	if(levelType){
		free(levelType);
	}
	levelType = strdup(type);
}

void Client::setServerMode(int mode){
	serverMode = mode;
}

void Client::setCurrentDimension(int dim){
	currentDimension = dim;
}

void Client::setDifficulty(int diff){
	difficulty = diff;
}

void Client::setMaxPlayers(int max){
	if(playersList){
		for(int i=0; i<maxPlayers; i++){
			free(playersList[i]);
		}
	}
	if(playersPing){
		free(playersPing);
	}
	maxPlayers = max;
	playersList = (char**)realloc(playersList, sizeof(char*) * maxPlayers);
	playersPing = (int*)malloc(sizeof(int) * maxPlayers);
	for(int i=0; i<maxPlayers; i++){
		playersList[i] = (char*)malloc(64);
		memset(playersList[i], 0, 64);
		playersPing[i] = 0;
	}
}

void Client::addPlayerList(const char* name){
	for(int i=0; i<maxPlayers; i++){
		if(!strcmp(playersList[i], name)){
			break;
		}
		if(playersList[i][0] == '\0'){
			strncpy(playersList[i], name, 64);
			break;
		}
	}
}

void Client::removePlayerList(const char* name){
	for(int i=0; i<maxPlayers; i++){
		if(!strcmp(playersList[i], name)){
			memset(playersList[i], 0, 64);
			break;
		}
	}
}

void Client::setPlayerPing(const char* name, short ping){
	for(int i=0; i<maxPlayers; i++){
		if(!strcmp(playersList[i], name)){
			playersPing[i] = ping;
			break;
		}
	}
}
